package com.moyuwanjia.utils;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * @author 摸鱼丸加~
 * @create 2022-07-13 14:51
 * 用于记录日志的工具类，提供了公共的代码
 * 如果使用的是spring-context5.2.x版本，其中会有通知方法调用乱序的bug，不过在后续版本中已被修复
 */
@Aspect//表示当前类是一个切面类
@Component("logger")
public class Logger {
    //同样在类中可以定义切入点表达式
    @Pointcut("execution(* com.moyuwanjia.service.impl.*.*(..))")
    public void pointcut01(){}

    /**
     * 用于打印日志：计划让其在切入点方法执行之前执行
     * 切入点方法：就是业务层中的方法
     * 前置通知
     */
    @Before("pointcut01()")
    public void beforePrintLog(){
        System.out.println("Logger中的beforePrintLog方法开始记录日志了。。。。");
    }

    /**
     * 后置通知
     */
    @AfterReturning("pointcut01()")
    public void afterReturnPrintLog(){
        System.out.println("Logger中的后置afterReturnPrintLog方法开始记录日志了。。。。");
    }
    /**
     * 异常通知
     */
    @AfterThrowing("pointcut01()")
    public void afterThrowingPrintLog(){
        System.out.println("Logger中的异常afterThrowingPrintLog方法开始记录日志了。。。。");
    }
    /**
     * 最终通知
     */
    @After("pointcut01()")
    public void afterPrintLog(){
        System.out.println("Logger中的最终afterPrintLog方法开始记录日志了。。。。");
    }

    /**
     * 测试环绕通知
     * 问题：当配置了环绕通知之后，切入点方法没有执行，而通知方法执行了。
     * 分析：通过对比动态代理中的环绕通知代码，发现动态代理的环绕通知方法有明确的切入点方法调用
     *      而我们的代码中没有
     * 解决：spring为我们提供一个接口：ProceedingJoinPoint,该接口有一个方法proceed(),此方法就相当于明确调用切入点方法。
     *      该接口可以作为环绕通知的方法参数，在程序执行时，spring框架会为我们提供该接口的实现类供我们使用
     * spring中的环绕通知：
     *      它是spring框架为我们提供的除了配置之外一种可以在代码中手动控制增强方法何时执行的方式。
     */
//    @Around("pointcut01()")
    public Object aroundPrintLog(ProceedingJoinPoint point){
        Object returnValue = null;
        try {
            Object[] args = point.getArgs();//得到方法执行所需要的参数
            System.out.println("Logger中的环绕aroundPrintLog方法开始记录日志了。。。。前置通知");
            returnValue = point.proceed(args);//明确调用业务层方法（切入点方法）
            System.out.println("Logger中的环绕aroundPrintLog方法开始记录日志了。。。。后置通知");
        } catch (Throwable throwable) {//这里使用Exception无法拦截该异常，应该使用异常的超类Throwable
            System.out.println("Logger中的环绕aroundPrintLog方法开始记录日志了。。。。异常通知");
            throwable.printStackTrace();
        } finally {
            System.out.println("Logger中的环绕aroundPrintLog方法开始记录日志了。。。。最终通知");
        }
        return  returnValue;
    }

}
